namespace EyeOpen
{
	using System;
	using System.Collections.Generic;
	using System.Linq;

	public class DependencyContainer
	{
		private static readonly DependencyContainer Instance = 
			new DependencyContainer();

		private IDependencyContainer current = 
			new DefaultDependencyContainer();
	
		public static IDependencyContainer Current
		{
			get
			{
				return Instance.InnerCurrent;
			}
		}

		public IDependencyContainer InnerCurrent
		{
			get
			{
				return current;
			}
		}

		public static void SetResolver(object commonServiceLocator)
		{
			Instance.InnerSetResolver(commonServiceLocator);
		}

		public static void SetResolver(IDependencyContainer resolver)
		{
			Instance.InnerSetResolver(resolver);
		}

		public static void SetResolver(Func<Type, object> getService, Func<Type, IEnumerable<object>> getServices)
		{
			Instance.InnerSetResolver(getService, getServices);
		}

		/// <exception cref="ArgumentNullException">commonServiceLocator</exception>
		/// <exception cref="ArgumentException">DependencyContainer does not implement ICommonServiceLocator.</exception>
		public void InnerSetResolver(object commonServiceLocator)
		{
			if (commonServiceLocator == null)
			{
				throw new ArgumentNullException("commonServiceLocator");
			}

			var type = commonServiceLocator.GetType();
			var method = type.GetMethod("GetInstance", new Type[] { typeof(Type) });
			var info2 = type.GetMethod("GetAllInstances", new Type[] { typeof(Type) });
		
			if (((method == null) || (method.ReturnType != typeof(object))) || ((info2 == null) || (info2.ReturnType != typeof(IEnumerable<object>))))
			{
				throw new ArgumentException("DependencyContainer does not implement ICommonServiceLocator.", "commonServiceLocator");
			}

			var getService =
				(Func<Type, object>)Delegate.CreateDelegate(typeof(Func<Type, object>), commonServiceLocator, method);

			var getServices =
				(Func<Type, IEnumerable<object>>)
				Delegate.CreateDelegate(typeof(Func<Type, IEnumerable<object>>), commonServiceLocator, info2);

			current = new DelegateBasedDependencyResolver(getService, getServices);
		}

		/// <exception cref="ArgumentNullException">resolver</exception>
		public void InnerSetResolver(IDependencyContainer resolver)
		{
			if (resolver == null)
			{
				throw new ArgumentNullException("resolver");
			}

			current = resolver;
		}

		/// <exception cref="ArgumentNullException">getService</exception>
		public void InnerSetResolver(Func<Type, object> getService, Func<Type, IEnumerable<object>> getServices)
		{
			if (getService == null)
			{
				throw new ArgumentNullException("getService");
			}

			if (getServices == null)
			{
				throw new ArgumentNullException("getServices");
			}

			current = new DelegateBasedDependencyResolver(getService, getServices);
		}

		private class DefaultDependencyContainer : IDependencyContainer
		{
			public object GetService(Type serviceType)
			{
				try
				{
					return Activator.CreateInstance(serviceType);
				}
				catch
				{
					return null;
				}
			}

			public IEnumerable<object> GetServices(Type serviceType)
			{
				return Enumerable.Empty<object>();
			}
		}

		private class DelegateBasedDependencyResolver : IDependencyContainer
		{
			private readonly Func<Type, object> getService;
			private readonly Func<Type, IEnumerable<object>> getServices;

			public DelegateBasedDependencyResolver(Func<Type, object> getService, Func<Type, IEnumerable<object>> getServices)
			{
				this.getService = getService;
				this.getServices = getServices;
			}

			public object GetService(Type type)
			{
				try
				{
					return getService(type);
				}
				catch
				{
					return null;
				}
			}

			public IEnumerable<object> GetServices(Type type)
			{
				return getServices(type);
			}
		}
	}
}